#include "glut_render.hpp"


#ifdef _WIN32

#include <windows.h>
#define delayms(x) Sleep(x)	/**< delay processing by x second */

#else //LINUX

#include <unistd.h>
#define delayms(x) usleep((x)*1000)	/**< delay processing by x second */

#endif

#define fps 60;
const int time_step = 1000 / fps;

GLubyte *
  glut_render::textureImage;
unsigned long
  glut_render::texHeight;
unsigned long
  glut_render::texWidth;
osc_control *
  glut_render::Instrument;
int8_t *
  glut_render::trace_horz;

int8_t
max (int8_t number[], unsigned count)
{
  int8_t
    max_no = number[0];

  for (int8_t * p = number + 1; (p - number) < count; p++)
    if (*p > max_no)
      max_no = *p;

  return max_no;
}


int8_t
min (int8_t number[], unsigned count)
{
  int8_t
    min_no = number[0];

  for (int8_t * p = number + 1; (p - number) < count; p++)
    if (*p < min_no)
      min_no = *p;

  return min_no;
}

glut_render::~glut_render ()
{
  delete[]textureImage;
  delete[]trace_horz;
}

glut_render::glut_render (int argc, char *argv[], unsigned long texWidth,
			  unsigned long texHeight, osc_control * Instrument)
{
  this->texWidth = texWidth;
  this->texHeight = texHeight;

  this->Instrument = Instrument;

  textureImage = new GLubyte[texHeight * texWidth];
  trace_horz = new int8_t[texWidth];

//=======================================================
  glutInit (&argc, argv);	// Initialize GLUT

// Allow postprocessing after window close
  glutSetOption (GLUT_ACTION_ON_WINDOW_CLOSE,
		 GLUT_ACTION_GLUTMAINLOOP_RETURNS);

  glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);	// Set up a basic display buffer (only single buffered for now)
  //glutInitWindowSize (500, 500);        // Set the width and height of the window
  //glutInitWindowPosition (100, 100);    // Set the position of the window
}

void
glut_render::open (char title[])
{
  glutCreateWindow (title);	// Open OpenGL Window

  init ();			// Initialize scenery
  display ();
  glutDisplayFunc (display);	// Tell GLUT to use the method "display" for rendering
  glutIdleFunc (update);
  //glutTimerFunc(time_step,update,0);

  glutReshapeFunc (reshape);	// Tell GLUT to use the method "reshape" for reshaping

  glutMainLoop ();		// Enter GLUT's main loop
}

void
glut_render::init ()
{
  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glEnable (GL_DEPTH_TEST);

  glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
  updateTextureImage ();

  glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glEnable (GL_TEXTURE_2D);
  glShadeModel (GL_FLAT);
}

void
glut_render::updateTextureImage ()
{
  static int line_idx = 0;

  int basetime = glutGet (GLUT_ELAPSED_TIME);
  int time = basetime;

  for (; line_idx < texHeight; line_idx++)
    {
//==================================================================
//******************************************************************
      Instrument->getTrace8 (trace_horz, texWidth);
      int8_t mini = min (trace_horz, texWidth);
      int8_t range = max (trace_horz, texWidth) - mini;
      for (int j = 0; j < texWidth; j++)
	*(textureImage + line_idx * texWidth + j) =
	  int (trace_horz[j] - mini) * 255 / range;
      //delayms(2);
//******************************************************************
//==================================================================

      time = glutGet (GLUT_ELAPSED_TIME);
      if (time - basetime > time_step)
	{
	  line_idx++;
	  break;
	}
    }

// Still too early
  if (time - basetime < time_step)
    delayms ((basetime + time_step - time));

// cycle back to the first scan line
  if (line_idx >= texHeight)
    line_idx = 0;

// Update texture
  glTexImage2D (GL_TEXTURE_2D, 0, 3, texWidth,
		texHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, textureImage);
}

/** Function call to render the first frame in OpenGL */
void
glut_render::display ()
{

  glLoadIdentity ();
  glTranslatef (0.0, 0.0, -1.8);
  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glBegin (GL_QUADS);

  glTexCoord2f (0.0, 0.0);
  glVertex2f (-1.0, -1.0);	// bottom right

  glTexCoord2f (0.0, 1.0);
  glVertex2f (-1.0, 1.0);	// top left

  glTexCoord2f (1.0, 1.0);
  glVertex2f (1.0, 1.0);	// top right

  glTexCoord2f (1.0, 0.0);
  glVertex2f (1.0, -1.0);	// bottom right

  glEnd ();

  glutSwapBuffers ();		//Send the 3D scene to the screen
}

/** Rescale frame when the window is resized */
void
glut_render::reshape (int width, int height)
{
  glViewport (0, 0, width, height);
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  gluPerspective (60.0, (double) width / (double) height, 1, 30);
  glMatrixMode (GL_MODELVIEW);
}

/** Function call to refresh frame in OpenGL */
void
glut_render::update ()
{
  updateTextureImage ();

  glutPostRedisplay ();		// Inform GLUT that the scene has changed

  //glutTimerFunc(time_step,update,0);
}
